﻿#region Using directives

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;

using System.Xml.Serialization;
using System.Windows.Forms;

using Weazel.Toad.Application.Actions;

#endregion

namespace Weazel.Toad.Application.Items
{
  [Serializable]
  public abstract class ItemBase
  {
    public event EventHandler<EventArgs> ItemDisplayNameChanged;
    public event EventHandler<EventArgs> ItemFilenameChanged;
    public event EventHandler<EventArgs> ItemChanged;
    public event EventHandler<EventArgs> ItemContentsChanged;

    public abstract string Extension
    {
      get;
    }

    public abstract string FileTypeName
    {
      get;
    }

    #region persisted properties

    private string displayName = "Unnamed Item";
    [ReadOnly(true)]
    [DisplayName("Display Name")]
    [Description("The Name shown for this Item")]
    public string DisplayName
    {
      get { return displayName; }
      set {
        if (displayName != value)
        {
          displayName = value;
          if (ItemDisplayNameChanged != null)
            ItemDisplayNameChanged(this, EventArgs.Empty);
        }
      }
    }

    private string filename;
    [ReadOnly(true)]
    [DisplayName("Filename")]
    [Description("The full Filename of this Item")]
    public string Filename
    {
      get { return filename; }
      set
      {
        if (filename != value)
        {
          filename = value;

          if (ItemFilenameChanged != null)
            ItemFilenameChanged(this, EventArgs.Empty);
        }
      }
    }

    #endregion

    #region non-persisted properties

    private bool canSave = true;
    public virtual bool CanSave
    {
      get { return canSave; }
    }


    protected bool hasUnsavedChanges;

    [XmlIgnore]
    public bool HasUnsavedChanges
    {
      get { return hasUnsavedChanges; }
      set { hasUnsavedChanges = value; }
    }

    protected TabPage tabPage;

    [XmlIgnore]
    public TabPage TabPage
    {
      get { return tabPage; }
    }

    #endregion

    public ItemBase()
    {
      if (Application.Instance.Project != null)      
        Application.Instance.Project.AddItem(this);      
    }

    public bool Close()
    {
      if (hasUnsavedChanges)
      {
        DialogResult result = 
          MessageBox.Show(
          string.Format("Save changes to {0}?", this.displayName), 
          "Save changes", 
          MessageBoxButtons.YesNoCancel, 
          MessageBoxIcon.Question
          );

        if (result == DialogResult.Yes)
        {
          // saven & close
          Save();
          Application.Instance.RemoveOpenItem(this);
          CloseInternal();
          CloseTabPage();
          
          return true;
        }
        else if (result == DialogResult.No)
        {
          // just close
          Application.Instance.RemoveOpenItem(this);
          CloseInternal();
          CloseTabPage();

          return true;
        }
        else
        {
          System.Diagnostics.Debug.Assert(DialogResult.Cancel == result);

          // user cancelled
          return false;
        }
      }

      // close - no unsaved changes
      Application.Instance.RemoveOpenItem(this);
      CloseInternal();
      CloseTabPage();
		      
      return true;
    }

    public void CloseTabPage()
    {      
      TabControl mainTabControl = Application.Instance.MainForm.MainTabControl;

      System.Diagnostics.Debug.Assert(this.tabPage != null);
      System.Diagnostics.Debug.Assert(mainTabControl.TabPages.Contains(this.tabPage));


      // undbind actions on tab page
      // (none so far)

      // remove tab page
      mainTabControl.TabPages.Remove(this.tabPage);
      this.tabPage.Tag = null; // allow gc
      this.tabPage = null;
    }

    protected virtual void CloseInternal()
    {
      // default, do nothing implementation
    }

    public bool Open()
    {
      if (filename == null)
      {
        OpenFileDialog openDialog = new OpenFileDialog();
        openDialog.AddExtension = true;
        openDialog.DefaultExt = Extension;
        openDialog.CheckFileExists = true;
        openDialog.Multiselect = false;
        openDialog.Filter = string.Format("{0} Files (*.{1})|*.{1}", FileTypeName, Extension);

        if (openDialog.ShowDialog() == DialogResult.OK)
        {
          this.Filename = openDialog.FileName;
          this.DisplayName = new System.IO.FileInfo(filename).Name;

          OpenTabPage();
          OpenInternal();
          hasUnsavedChanges = false;

          // call item changed method to give the
          // item a change to notify anybody who
          // might be interrested in it
          OnItemChanged();

          return true;
        }
        else
        {
          return false;
        }
      }
      else
      {
        // has filename, just open
        OpenTabPage();
        OpenInternal();
        hasUnsavedChanges = false;

        // call item changed method to give the
        // item a change to notify anybody who
        // might be interrested in it
        OnItemChanged();

        return true;
      }
    }

    public void Save()
    {
      System.Diagnostics.Debug.Assert(this.CanSave);
      if (filename == null)
      {
        SaveFileDialog saveDialog = new SaveFileDialog();
        saveDialog.AddExtension = true;
        saveDialog.DefaultExt = Extension;
        saveDialog.OverwritePrompt = true;
        saveDialog.ValidateNames = true;
        saveDialog.FileName = this.displayName;

        if (saveDialog.ShowDialog() == DialogResult.OK)
        {
          this.Filename = saveDialog.FileName;
          this.DisplayName = new System.IO.FileInfo(filename).Name;



          SaveInternal();
          hasUnsavedChanges = false;

          // call item changed method to give the
          // item a change to notify anybody who
          // might be interrested in it
          OnItemChanged();
        }
      }
      else
      {
        // has filename, just save
        SaveInternal();
        hasUnsavedChanges = false;

        // call item changed method to give the
        // item a change to notify anybody who
        // might be interrested in it
        OnItemChanged();
      }
    }

    public void SaveAs()
    {
      System.Diagnostics.Debug.Assert(this.CanSave);
      SaveFileDialog saveDialog = new SaveFileDialog();
      saveDialog.AddExtension = true;
      saveDialog.DefaultExt = Extension;
      saveDialog.OverwritePrompt = true;
      saveDialog.ValidateNames = true;
      saveDialog.FileName = this.displayName;

      if (saveDialog.ShowDialog() == DialogResult.OK)
      {
        this.Filename = saveDialog.FileName;
        this.DisplayName = new System.IO.FileInfo(filename).Name;

        SaveInternal();
        hasUnsavedChanges = false;

        // call item changed method to give the
        // item a change to notify anybody who
        // might be interrested in it
        OnItemChanged();
      }
    }

    public void OpenTabPage()
    {
      TabControl mainTabControl = Application.Instance.MainForm.LayoutControl.MainTabControl;

      foreach (TabPage tabPage in mainTabControl.TabPages)
      {
        if (tabPage.Tag == this)
        {
          mainTabControl.SelectedTab = tabPage;
          return;
        }
      }

      // setup context menu below
      // (none so far)

      // tab page did not exist
      this.tabPage = new TabPage(this.displayName);
      this.tabPage.Tag = this;

      OpenTabPageInternal();

      // add to application before tabcontrol. the tabcontrol's
      // OnSelectIndexChanged event will expect to find this item
      // in the list of open items.
      Application.Instance.AddOpenItem(this);

      mainTabControl.TabPages.Add(this.tabPage);
      mainTabControl.SelectedTab = this.tabPage;      
    }

    /// <summary>
    /// Default implementation which does nothing
    /// </summary>
    protected virtual void SaveInternal()
    {
      throw new NotSupportedException();
    }

    /// <summary>
    /// Default implementation which does nothing
    /// </summary>
    protected virtual void OpenInternal()
    {
      throw new NotSupportedException();
    }

    protected virtual void OpenTabPageInternal()
    {
      throw new NotImplementedException();
    }

    protected virtual void OnItemChanged()
    {
      if (ItemChanged != null)
        ItemChanged(this, EventArgs.Empty);

      if (tabPage != null)
      {
        tabPage.Text = this.displayName;
      }
    }

    protected void OnItemContentsChanged()
    {
      this.hasUnsavedChanges = true;
      if (ItemContentsChanged != null)
      {
        ItemContentsChanged(this, EventArgs.Empty);
      }
    }
  }
}
